结构型模式-代理模式
代理模式
classDiagram
Client --> Subject
Subject <|.. RealSubject
Subject <|.. Proxy
Proxy --> RealSubject
Subject: <<interface>>
Subject: +request()
RealSubject: +request()
Proxy: -RealSubject realSubject
Proxy: +Proxy(RealSubject)
Proxy: +request()
静态代理
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("这是被代理对象执行的方法");
}
}
public class Proxy implements Subject {
private RealSubject realSubject;
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
System.out.println("这是代理执行的方法");
realSubject.request();
}
}
// demo
public static void main(String[] args) {
Proxy proxy = new Proxy(new RealSubject());
proxy.request();
}
动态代理
这里只介绍 JDK 自带的代理工具类
首先了解如下几个类:
Proxy
专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。
提供了许多用于创建动态代理类和动态代理对象的静态方法。
// 关键就是这个 newProxyInstance 方法
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
这里需要传入一个 InvocationHandler 对象
InvocationHandler
InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
InvocationHandler 内部只有一个 invoke()
方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。
- proxy:当前代理实例,一般不用它,因为调用了它的方法,会在这里被反射调用,导致递归从而死循环
- method:代理对象调用的方法
- args:调用的方法中的参数
因为,Proxy 动态产生的代理对象会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。
使用例
故而,总的运行流程为:获取代理实例 -–> 代理实例.方法 --> InvocationHandler.invoke()
--> 接口真正实现类的方法。
public class Temp {
public static void main(String[] args) {
Human human = new Everyman();
human.info("张三");
human.walk();
System.out.println("======================================================");
// 生成代理对象
Human proxyInstance = (Human)Proxy.newProxyInstance(
human.getClass().getClassLoader(),
human.getClass().getInterfaces(),
(Object proxy, Method method, Object[] parameter) -> {
if ("info".equals(method.getName())) {
// 这里直接调用了上面创建的实例对象,一般来说是创建一个 InvocationHandler 实现类,通过构造方法传进来的
method.invoke(human, parameter);
System.out.println("现在我被代理了,所以现在我是代理人!我的名字是:" + parameter[0]);
}
if ("walk".equals(method.getName())) {
method.invoke(human, parameter);
System.out.println("现在我被代理了,所以现在我能跑 200米不带喘!");
}
return proxy;
});
proxyInstance.info("李四");
proxyInstance.walk();
}
// 还是需要先定义一个接口
interface Human {
void info(String name);
void walk();
}
static class Everyman implements Human{
@Override
public void info(String name) {
System.out.println("我是个普通人,我的名字是:" + name);
}
@Override
public void walk() {
System.out.println("我能跑 50米不带喘");
}
}
}
输出为:
我是个普通人,我的名字是:张三
我能跑 50米不带喘
======================================================
我是个普通人,我的名字是:李四
现在我被代理了,所以现在我是代理人!我的名字是:李四
我能跑 50米不带喘
现在我被代理了,所以现在我能跑 200米不带喘!
一般是单独实现 InvocationHandler 这个接口,这样可以使用构造方法,把被代理对象传进来,而不是上面例子那样直接把实例对象丢到匿名内部类里面执行